home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 2.toast / pc / sample code / quicktime / quicktime vr / vrscript / feature files / vreffects.c < prev    next >
Encoding:
Text File  |  2000-09-28  |  17.4 KB  |  562 lines

  1. //////////
  2. //
  3. //    File:        VREffects.c
  4. //
  5. //    Contains:    QuickTime video effects support for QuickTime VR movies.
  6. //
  7. //    Written by:    Tim Monroe
  8. //                Parts modeled on ShowEffect sample code by Dan Crow.
  9. //
  10. //    Copyright:    © 1997-1998 by Apple Computer, Inc., all rights reserved.
  11. //
  12. //    Change History (most recent first):
  13. //
  14. //       <9>         06/17/99    rtm        added HLock and HUnlock calls to VREffects_RunTransitionEffect
  15. //       <8>         05/06/99    rtm        added support for MakeImageDescriptionForEffect (QT 4.0 and later)
  16. //       <7>         04/24/98    rtm        added VRMoov_DoIdle call to VREffects_RunTransitionEffect, to service
  17. //                                    scene-wide sounds while a transition is occurring 
  18. //       <6>         02/26/98    rtm        reworked VREffects_SetupTransitionEffect to use QTVRUpdate, not CopyBits;
  19. //                                    now we work much better on Windows (finally!);
  20. //                                    added VRScript_CheckForExpiredCommand call to VREffects_RunTransitionEffect
  21. //       <5>         02/10/98    rtm        fixed source rectangles in VREffects_SetupTransitionEffect
  22. //       <4>         02/05/98    rtm        cleaned up GWorld handling; remember, lock those pixmaps!
  23. //       <3>         11/01/97    rtm        added Endian macros to VREffects_MakeEffectDescription
  24. //       <2>         10/29/97    rtm        further work: got node transition effects working on Mac
  25. //       <1>         10/27/97    rtm        first file
  26. //       
  27. //    This file provides functions to incorporate QuickTime video effects into QuickTime VR movies.
  28. //    QuickTime video effects were introduced with QuickTime 3.0; they support many SMPTE transitions
  29. //    and other special effects. This file defines functions that use QT video effects in these ways:
  30. //
  31. //    * run effects while transitioning between nodes (let's call these "transition effects")
  32. //    * (this space for rent)
  33. //
  34. //////////
  35.  
  36. //    TO DO:
  37. //    + implement parameter handling
  38. //    + currently only one transition per node pair is supported; allow multiple transition effects
  39.  
  40. // header files
  41. #include "VREffects.h"
  42.  
  43.  
  44. //////////
  45. //
  46. // VREffects_InitWindowData
  47. // Initialize for QuickTime video effects.
  48. //
  49. //////////
  50.  
  51. void VREffects_InitWindowData (WindowObject theWindowObject)
  52. {
  53.     ApplicationDataHdl            myAppData;
  54.  
  55.     myAppData = (ApplicationDataHdl)GetAppDataFromWindowObject(theWindowObject);
  56.     if (myAppData != NULL) {
  57.         (**myAppData).fSourceGWorld = NULL;
  58.         (**myAppData).fTargetGWorld = NULL;
  59.         (**myAppData).fSourceGWDesc = NULL;
  60.         (**myAppData).fTargetGWDesc = NULL;
  61.         (**myAppData).fActiveTransition = NULL;
  62.     }
  63. }
  64.  
  65.  
  66. //////////
  67. //
  68. // VREffects_DumpWindowData
  69. // Dump the window-specific data for QuickTime video effects.
  70. //
  71. //////////
  72.  
  73. void VREffects_DumpWindowData (WindowObject theWindowObject)
  74. {
  75.     ApplicationDataHdl            myAppData;
  76.     
  77.     myAppData = (ApplicationDataHdl)GetAppDataFromWindowObject(theWindowObject);
  78.     if (myAppData != NULL) {
  79.  
  80.         // dispose of transition effects GWorlds used by this window object
  81.         if ((**myAppData).fSourceGWorld != NULL) {
  82.             DisposeGWorld((**myAppData).fSourceGWorld);
  83.             (**myAppData).fSourceGWorld = NULL;
  84.         }
  85.         
  86.         if ((**myAppData).fTargetGWorld != NULL) {
  87.             DisposeGWorld((**myAppData).fTargetGWorld);
  88.             (**myAppData).fTargetGWorld = NULL;
  89.         }
  90.         
  91.         // dispose of the image descriptions
  92.         if ((**myAppData).fSourceGWDesc != NULL) {
  93.             DisposeHandle((Handle)(**myAppData).fSourceGWDesc);
  94.             (**myAppData).fSourceGWDesc = NULL;
  95.         }
  96.         
  97.         if ((**myAppData).fTargetGWDesc != NULL) {
  98.             DisposeHandle((Handle)(**myAppData).fTargetGWDesc);
  99.             (**myAppData).fTargetGWDesc = NULL;
  100.         }
  101.         
  102.     }
  103. }
  104.  
  105.  
  106. //////////
  107. //
  108. // VREffects_DoIdle
  109. // Do any effects-related processing that can or should occur at idle time.
  110. // Returns true if the caller should call QTVRUpdate, false otherwise.
  111. //
  112. //////////
  113.  
  114. Boolean VREffects_DoIdle (WindowObject theWindowObject)
  115. {
  116.     ApplicationDataHdl        myAppData;
  117.     Boolean                    myNeedUpdate = false;
  118.  
  119.     myAppData = (ApplicationDataHdl)GetAppDataFromWindowObject(theWindowObject);
  120.     if (myAppData != NULL) {
  121.     
  122.         // nothing needed for transition effects
  123.         
  124.     }
  125.     
  126.     return(myNeedUpdate);
  127. }
  128.         
  129.                 
  130. //////////
  131. //
  132. // VREffects_GetTransitionEffect
  133. // Return the first transition effect enlisted for the specified fromNode/toNode pair.
  134. //
  135. //////////
  136.  
  137. VRScriptTransitionPtr VREffects_GetTransitionEffect (WindowObject theWindowObject, UInt32 fromNodeID, UInt32 toNodeID)
  138. {
  139.     ApplicationDataHdl        myAppData = NULL;
  140.     VRScriptTransitionPtr    myPointer = NULL;
  141.     VRScriptTransitionPtr    myNext = NULL;
  142.     
  143.     myAppData = (ApplicationDataHdl)GetAppDataFromWindowObject(theWindowObject);
  144.     if (myAppData == NULL)
  145.         return(myPointer);
  146.         
  147.     // walk the transition effects list and see if any apply to this node
  148.     myPointer = (VRScriptTransitionPtr)(**myAppData).fListArray[kVREntry_TransitionEffect];
  149.     while (myPointer != NULL) {
  150.         myNext = myPointer->fNextEntry;
  151.         
  152.         if ((myPointer->fFromNodeID == fromNodeID) || (myPointer->fFromNodeID == kVRAnyNode))
  153.             if ((myPointer->fToNodeID == toNodeID) || (myPointer->fToNodeID == kVRAnyNode))
  154.                 return(myPointer);
  155.     
  156.         myPointer = myNext;
  157.     }
  158.     
  159.     return(myPointer);
  160. }
  161.  
  162.  
  163. //////////
  164. //
  165. // VREffects_MakeEffectDescription
  166. // Create an effect description.
  167. //
  168. // The effect description specifies which video effect is desired and the parameters for that effect.
  169. // It also describes the source(s) for the effect. An effect description is simply an atom container
  170. // that holds atoms with the appropriate information.
  171. //
  172. // Note that because we are creating an atom container, we must pass big-endian data (hence the calls
  173. // to EndianU32_NtoB).
  174. //
  175. // Currently we support up to two sources.
  176. //
  177. //////////
  178.  
  179. QTAtomContainer VREffects_MakeEffectDescription (OSType theEffectType, long theEffectNum, OSType theSourceName1, OSType theSourceName2)
  180. {
  181.     QTAtomContainer        mySample = NULL;
  182.     OSType                myType;
  183.     OSErr                myErr = noErr;
  184.  
  185.     // create a new, empty effect description
  186.     myErr = QTNewAtomContainer(&mySample);
  187.     if (myErr != noErr)
  188.         return(NULL);
  189.     
  190.     // create the effect ID atom: the atom type is kParameterWhatName, and the atom ID is kParameterWhatID
  191.     myType = EndianU32_NtoB(theEffectType);
  192.     myErr = QTInsertChild(mySample, kParentAtomIsContainer, kParameterWhatName, kParameterWhatID, 0, sizeof(myType), &myType, NULL);
  193.     if (myErr != noErr)
  194.         return(NULL);
  195.         
  196.     // add the first source, if it's not kSourceNoneName
  197.     if (theSourceName1 != kSourceNoneName) {
  198.         myType = EndianU32_NtoB(theSourceName1);
  199.         myErr = QTInsertChild(mySample, kParentAtomIsContainer, kEffectSourceName, 1, 0, sizeof(myType), &myType, NULL);
  200.     }
  201.                                 
  202.     // add the second source, if it's not kSourceNoneName
  203.     if (theSourceName2 != kSourceNoneName) {
  204.         myType = EndianU32_NtoB(theSourceName2);
  205.         myErr = QTInsertChild(mySample, kParentAtomIsContainer, kEffectSourceName, 2, 0, sizeof(myType), &myType, NULL);
  206.     }
  207.     
  208.     // add a parameter for SMPTE effects
  209.     myType = EndianU32_NtoB(theEffectNum);
  210.     myErr = QTInsertChild(mySample, kParentAtomIsContainer, FOUR_CHAR_CODE('wpID'), 1, 0, sizeof(myType), &myType, NULL);
  211.     
  212.     return(mySample);
  213. }
  214.  
  215.  
  216. //////////
  217. //
  218. // VREffects_MakeSampleDescription
  219. // Create a description of an effect sample.
  220. //
  221. //////////
  222.  
  223. ImageDescriptionHandle VREffects_MakeSampleDescription (OSType theEffectType, short theWidth, short theHeight)
  224. {
  225.     ImageDescriptionHandle        mySampleDesc = NULL;
  226.  
  227. #if USES_MAKE_IMAGE_DESC_FOR_EFFECT
  228.     OSErr                        myErr = noErr;
  229.     
  230.     // create a new sample description
  231.     myErr = MakeImageDescriptionForEffect(theEffectType, &mySampleDesc);
  232.     if (myErr != noErr)
  233.         return(NULL);
  234. #else
  235.     // create a new sample description
  236.     mySampleDesc = (ImageDescriptionHandle)NewHandleClear(sizeof(ImageDescription));
  237.     if (mySampleDesc == NULL)
  238.         return(NULL);
  239.     
  240.     // fill in the fields of the sample description
  241.     (**mySampleDesc).cType = theEffectType;
  242.     (**mySampleDesc).idSize = sizeof(ImageDescription);
  243.     (**mySampleDesc).hRes = 72L << 16;
  244.     (**mySampleDesc).vRes = 72L << 16;
  245.     (**mySampleDesc).dataSize = 0L;
  246.     (**mySampleDesc).frameCount = 1;
  247.     (**mySampleDesc).depth = 0;
  248.     (**mySampleDesc).clutID = -1;
  249. #endif
  250.  
  251.     (**mySampleDesc).vendor = kAppleManufacturer;
  252.     (**mySampleDesc).temporalQuality = codecNormalQuality;
  253.     (**mySampleDesc).spatialQuality = codecNormalQuality;
  254.     (**mySampleDesc).width = theWidth;
  255.     (**mySampleDesc).height = theHeight;
  256.     
  257.     return(mySampleDesc);
  258. }
  259.  
  260.  
  261. //////////
  262. //
  263. // VREffects_SetupTransitionEffect
  264. // Prepare for a transition from the current node to the target node:
  265. // * make a copy of the current screen image
  266. // * set movie and controller GWorlds to an offscreen graphics world
  267. //
  268. //////////
  269.  
  270. OSErr VREffects_SetupTransitionEffect (WindowObject theWindowObject, UInt32 fromNodeID, UInt32 toNodeID)
  271. {
  272.     ApplicationDataHdl        myAppData = NULL;
  273.     VRScriptTransitionPtr    myPointer = NULL;
  274.     Rect                    myRect;
  275.     OSErr                    myErr = noErr;
  276.  
  277.     myAppData = (ApplicationDataHdl)GetAppDataFromWindowObject(theWindowObject);
  278.     if (myAppData == NULL)
  279.         return(paramErr);
  280.         
  281.     if (((**theWindowObject).fMovie == NULL) || ((**theWindowObject).fController == NULL))
  282.         return(paramErr);
  283.         
  284.     // lock the application data handle
  285.     HLock((Handle)myAppData);
  286.     
  287.     // see if any transition effects are enlisted for the current fromNode/toNode pair
  288.     myPointer = VREffects_GetTransitionEffect(theWindowObject, fromNodeID, toNodeID);
  289.     (**myAppData).fActiveTransition = myPointer;
  290.     if (myPointer == NULL)
  291.         goto bail;        // if there is no active transition effect, just bail
  292.  
  293.     // make sure our slate is clean
  294.     VREffects_DumpWindowData(theWindowObject);
  295.     VREffects_DumpEntryMem(myPointer);
  296.  
  297.     // get the size of the movie
  298.     GetMovieBox((**theWindowObject).fMovie, &myRect);
  299.     MacOffsetRect(&myRect, -myRect.left, -myRect.top);
  300.     
  301.     // create effect and sample descriptions            
  302.     myPointer->fEffectDesc = VREffects_MakeEffectDescription(myPointer->fEffectType, myPointer->fEffectNum, kSourceOneName, kSourceTwoName);
  303.     myPointer->fSampleDesc = VREffects_MakeSampleDescription(myPointer->fEffectType, myRect.right, myRect.bottom);
  304.     if ((myPointer->fEffectDesc == NULL) || (myPointer->fSampleDesc == NULL))
  305.         goto bail;
  306.  
  307.     // create an offscreen GWorld that is the size and depth of the movie window;
  308.     // this is the source of the transition effect
  309.     myErr = NewGWorld(&(**myAppData).fSourceGWorld, 0, &myRect, NULL, NULL, 0L);
  310.     if (myErr != noErr)
  311.         goto bail;
  312.  
  313.     LockPixels(GetGWorldPixMap((**myAppData).fSourceGWorld));    
  314.     
  315.     // create an offscreen GWorld that is the size and depth of the movie window;
  316.     // this is the destination of the transition effect
  317.     myErr = NewGWorld(&(**myAppData).fTargetGWorld, 0, &myRect, NULL, NULL, 0L);
  318.     if (myErr != noErr)
  319.         goto bail;
  320.  
  321.     LockPixels(GetGWorldPixMap((**myAppData).fTargetGWorld));
  322.     
  323.     // draw the current screen image into the source offscreen GWorld
  324.     SetMovieGWorld((**theWindowObject).fMovie, (CGrafPtr)(**myAppData).fSourceGWorld, NULL);
  325.     QTVRUpdate((**theWindowObject).fInstance, kQTVRCurrentMode);
  326.         
  327.     // set the movie and controller GWorlds to the destination GWorld
  328.     MCSetControllerPort((**theWindowObject).fController, (CGrafPtr)(**myAppData).fTargetGWorld);
  329.     SetMovieGWorld((**theWindowObject).fMovie, (CGrafPtr)(**myAppData).fTargetGWorld, NULL);
  330.     MCMovieChanged((**theWindowObject).fController, (**theWindowObject).fMovie);
  331.     
  332. bail:
  333.     // make sure we've got no transition scheduled if an error occurred
  334.     if (myErr != noErr)
  335.         (**myAppData).fActiveTransition = NULL;
  336.  
  337.     // unlock the application data handle
  338.     HUnlock((Handle)myAppData);
  339.     
  340.     return(myErr);
  341. }
  342.  
  343.  
  344. //////////
  345. //
  346. // VREffects_RunTransitionEffect
  347. // Run a transition from the previous node to the current node.
  348. //
  349. //////////
  350.  
  351. OSErr VREffects_RunTransitionEffect (WindowObject theWindowObject)
  352. {
  353.     ApplicationDataHdl            myAppData = NULL;
  354.     VRScriptTransitionPtr        myPointer = NULL;
  355.     ImageSequenceDataSource        mySrc1 = 0;
  356.     ImageSequenceDataSource        mySrc2 = 0;
  357.     ICMFrameTimeRecord            myFrameTime;
  358.     TimeValue                    myFrame;
  359.     PixMapHandle                mySrcPixMap;
  360.     PixMapHandle                myDstPixMap;
  361.     OSErr                        myErr = paramErr;
  362.  
  363.     myAppData = (ApplicationDataHdl)GetAppDataFromWindowObject(theWindowObject);
  364.     if (myAppData == NULL)
  365.         goto bail;
  366.         
  367.     HLock((Handle)myAppData);
  368.  
  369.     // if there is no active transition effect, just bail
  370.     myPointer = (**myAppData).fActiveTransition;
  371.     if (myPointer == NULL)
  372.         goto bail;
  373.     
  374.     if ((myPointer->fEffectDesc == NULL) || (myPointer->fSampleDesc == NULL))
  375.         goto bail;
  376.         
  377.     // make an effects sequence
  378.     HLock((Handle)myPointer->fEffectDesc);
  379.     
  380.     // prepare the decompression sequence for playback    
  381.     myErr = DecompressSequenceBeginS(
  382.                             &(myPointer->fSequenceID),
  383.                             myPointer->fSampleDesc,
  384.                             StripAddress(*((Handle)myPointer->fEffectDesc)),
  385.                             GetHandleSize((Handle)myPointer->fEffectDesc),
  386.                             (CGrafPtr)GetPortFromWindowReference((**theWindowObject).fWindow),
  387.                             NULL,
  388.                             NULL,
  389.                             NULL,
  390.                             ditherCopy,
  391.                             NULL,
  392.                             0,
  393.                             codecNormalQuality,
  394.                             NULL);
  395.  
  396.     HUnlock((Handle)myPointer->fEffectDesc);
  397.     if (myErr != noErr)
  398.         goto bail;
  399.  
  400.     // get the pixel maps for the GWorlds (which were already locked in VREffects_SetupTransitionEffect)
  401.     mySrcPixMap = GetGWorldPixMap((**myAppData).fSourceGWorld);
  402.     myDstPixMap = GetGWorldPixMap((**myAppData).fTargetGWorld);
  403.     if ((mySrcPixMap == NULL) || (myDstPixMap == NULL))
  404.         goto bail;
  405.  
  406.     // make the effect sources
  407.     MakeImageDescriptionForPixMap(mySrcPixMap, &(**myAppData).fSourceGWDesc);
  408.     myErr = CDSequenceNewDataSource(
  409.                             myPointer->fSequenceID,
  410.                             &mySrc1,
  411.                             kSourceOneName,
  412.                             1,
  413.                             (Handle)(**myAppData).fSourceGWDesc,
  414.                             NULL,
  415.                             0);
  416.     if (myErr != noErr)
  417.         goto bail;
  418.  
  419.     CDSequenceSetSourceData(mySrc1, GetPixBaseAddr(mySrcPixMap), (**(**myAppData).fSourceGWDesc).dataSize);
  420.  
  421.     MakeImageDescriptionForPixMap(myDstPixMap, &(**myAppData).fTargetGWDesc);
  422.     myErr = CDSequenceNewDataSource(
  423.                             myPointer->fSequenceID,
  424.                             &mySrc2,
  425.                             kSourceTwoName,
  426.                             1,
  427.                             (Handle)(**myAppData).fTargetGWDesc,
  428.                             NULL,
  429.                             0);
  430.     if (myErr != noErr)
  431.         goto bail;
  432.  
  433.     CDSequenceSetSourceData(mySrc2, GetPixBaseAddr(myDstPixMap), (**(**myAppData).fTargetGWDesc).dataSize);
  434.  
  435.     // dispose of any existing time base
  436.     if (myPointer->fTimeBase != NULL)
  437.         DisposeTimeBase(myPointer->fTimeBase);
  438.         
  439.     // create a new time base and associate it with the decompression sequence
  440.     // (this time base MUST be disposed of before the application exits or bad
  441.     // things may happen; we dispose of it when we call VREffects_DumpEntryMem
  442.     // below)
  443.     myPointer->fTimeBase = NewTimeBase();
  444.     myErr = GetMoviesError();
  445.     if (myErr != noErr)
  446.         goto bail;
  447.         
  448.     SetTimeBaseRate(myPointer->fTimeBase, 0);
  449.     myErr = CDSequenceSetTimeBase(myPointer->fSequenceID, myPointer->fTimeBase);
  450.     if (myErr != noErr)
  451.         goto bail;
  452.  
  453.     HLock((Handle)myPointer->fEffectDesc);
  454.     
  455.     // now run the effect thru from start to finish
  456.     for (myFrame = 1; myFrame <= myPointer->fNumberOfSteps; myFrame++) {
  457.  
  458.         // set the timebase time to the step of the sequence to be rendered
  459.         SetTimeBaseValue(myPointer->fTimeBase, myFrame, myPointer->fNumberOfSteps);
  460.         
  461.         myFrameTime.value.hi                = 0;
  462.         myFrameTime.value.lo                = myFrame;
  463.         myFrameTime.scale                    = myPointer->fNumberOfSteps;
  464.         myFrameTime.base                    = 0;
  465.         myFrameTime.duration                = myPointer->fNumberOfSteps;
  466.         myFrameTime.rate                    = 0;
  467.         myFrameTime.recordSize                = sizeof(myFrameTime);
  468.         myFrameTime.frameNumber                = 1;
  469.         myFrameTime.flags                    = icmFrameTimeHasVirtualStartTimeAndDuration;
  470.         myFrameTime.virtualStartTime.hi        = 0;
  471.         myFrameTime.virtualStartTime.lo        = 0;
  472.         myFrameTime.virtualDuration            = myPointer->fNumberOfSteps;
  473.         
  474.         
  475.         myErr = DecompressSequenceFrameWhen(myPointer->fSequenceID,
  476.                                             StripAddress(*((Handle)myPointer->fEffectDesc)),
  477.                                             GetHandleSize((Handle)myPointer->fEffectDesc),
  478.                                             0,
  479.                                             0,
  480.                                             NULL,
  481.                                             &myFrameTime);
  482.         if (myErr != noErr)
  483.             goto bail;
  484.             
  485.         // on very long transitions (or very slow machines), scene-wide sound-only movies need to be serviced
  486.         // (but not every frame, eh?)
  487.         if (myFrame % kDoIdleStep == 0)
  488.             VRMoov_DoIdle(theWindowObject);
  489.     }
  490.  
  491.     // see whether the enlisted effect has expired
  492.     VRScript_CheckForExpiredCommand(theWindowObject, (VRScriptGenericPtr)myPointer);
  493.     
  494.     // if we got this far, all is okay
  495.     myErr = noErr;
  496.     
  497. bail:
  498.     if ((myPointer != NULL) && (myPointer->fEffectDesc != NULL))
  499.         HUnlock((Handle)myPointer->fEffectDesc);
  500.     
  501.     // dispose of the effects GWorlds, if necessary
  502.     VREffects_DumpWindowData(theWindowObject);
  503.     
  504.     // dispose of the effect sequence and time base
  505.     VREffects_DumpEntryMem(myPointer);
  506.     
  507.     // reset the movie and controller GWorlds to the on-screen window
  508.     MCSetControllerPort((**theWindowObject).fController, (CGrafPtr)GetPortFromWindowReference((**theWindowObject).fWindow));
  509.     SetMovieGWorld((**theWindowObject).fMovie, (CGrafPtr)GetPortFromWindowReference((**theWindowObject).fWindow), NULL);
  510.     MCMovieChanged((**theWindowObject).fController, (**theWindowObject).fMovie);
  511.     
  512.     // mark this transition as having been run
  513.     (**myAppData).fActiveTransition = NULL;
  514.     
  515.     if (mySrcPixMap != NULL)
  516.         UnlockPixels(mySrcPixMap);
  517.         
  518.     if (myDstPixMap != NULL)
  519.         UnlockPixels(myDstPixMap);
  520.         
  521.     if (myAppData != NULL)
  522.         HUnlock((Handle)myAppData);
  523.  
  524.     return(myErr);
  525. }
  526.  
  527.  
  528. //////////
  529. //
  530. // VREffects_DumpEntryMem
  531. // Release any memory associated with the specified list entry.
  532. //
  533. //////////
  534.  
  535. void VREffects_DumpEntryMem (VRScriptTransitionPtr theEntry)
  536. {    
  537.     if (theEntry != NULL) {
  538.         // if an effect sequence is currently set up, end it
  539.         if (theEntry->fSequenceID != 0L) {
  540.             CDSequenceEnd(theEntry->fSequenceID);
  541.             theEntry->fSequenceID = 0L;
  542.         }
  543.             
  544.         // if a time base currently exists, dispose of it
  545.         if (theEntry->fTimeBase != NULL) {
  546.             DisposeTimeBase(theEntry->fTimeBase);
  547.             theEntry->fTimeBase = NULL;
  548.         }
  549.         
  550.         // dispose of the effect description
  551.         if (theEntry->fEffectDesc != NULL) {
  552.             QTDisposeAtomContainer(theEntry->fEffectDesc);
  553.             theEntry->fEffectDesc = NULL;
  554.         }
  555.         
  556.         // dispose of the sample description
  557.         if (theEntry->fSampleDesc != NULL) {
  558.             DisposeHandle((Handle)theEntry->fSampleDesc);
  559.             theEntry->fSampleDesc = NULL;
  560.         }
  561.     }
  562. }